Don't be frightened by the stacks of parentheses. It's all recursive. That last \
list, for example, contains three expressions: the atom ", (atomstring) "hello", ", the atom ", (atomstring) "mr", ", and \
the list ", (atomstring) "((operator))", ". Which, itself, is one-term list containing only the list \
", (atomstring) "(operator)", ". Which is, itself, a one-term list containing only the atom ", (atomstring) "operator", ". \
Get it?^^";
print "By the way, remember that an expression is either a single atom or a single list. \
", (atomstring) "1", " ", (atomstring) "2", " ", (atomstring) "3", " is three separate expressions, not any funky kind of parenthesis-stripped \
list or anything. If you type several expressions at the Scheme prompt, the \
interpreter will evaluate them one at a time.^^";
print_trans("1 2 3 four",
"1");
print_trans(2, "2");
print_trans(2, "3");
print_trans(2, "[Error: undefined atom: four]");
new_line;
];
[ Chapter4;
print_title ("4. Functions");
print "We've seen that numeric atoms evaluate to themselves, and other atoms seem to \
produce raging error messages. What does a list evaluate to? Aha! You've heard of \
functions? I really hope so. Anyway, when you evaluate a list, you're calling a \
function. The first element of the list is the function; the rest of the elements \
are its arguments.^^";
print_trans("(+ 1 1)",
"2");
new_line;
print "Right! Addition! Let's take a closer look. We typed in an expression: \
", (atomstring) "(+ 1 1)", ". Nothing magic there; it's a list containing three atoms, of which \
the first is a plus sign and the last two are both the number 1. The Scheme \
interpreter evaluates all of these. The plus sign means addition; the ", (atomstring) "1", " atoms \
both evaluate to themselves. So the interpreter runs the addition function, and \
hands it a pair of ", (atomstring) "1", " atoms to work with. One plus one is two, so the function \
returns the expression ", (atomstring) "2", ". And that's what ", (atomstring) "(+ 1 1)", " evaluates to.^^";
print "Sneaky people will raise their hands at this point. What am I trying to pull? \
~The plus sign means addition?~ Ok, ok. What I mean is, the atom ", (atomstring) "+", " is not \
one of those pernicious undefined atoms. It ", (emphstring) "is", " defined. Its value is the \
addition function. Here, try it:^^";
print_trans("+",
"[function]");
new_line;
print "The interpreter doesn't try to actually print out the addition function, because \
that's a bunch of internal program code. All functions print out looking like \
", (atomstring) "[function]", ".^^";
print_trans("(- 1 1)",
"0");
print_trans("-",
"[function]");
new_line;
print "You can probably guess what function ", (atomstring) "-", " evaluates to.^^";
print "Did I say that the interpreter evaluates ", (emphstring) "all", " the terms of a list? Indeed I \
did. This is important. Guess what ", (atomstring) "(+ (+ 1 1) 4)", " evaluates to? Ok, it's not too \
hard, but we'll go through it step by step anyway. It's a three-term list. The \
first term is ", (atomstring) "+", ", which evaluates to the addition function. The second term is \
the list ", (atomstring) "(+ 1 1)", ", and we've already discovered that evaluates to ", (atomstring) "2", ". The third \
term is ", (atomstring) "4", ", and that evaluates to ", (atomstring) "4", ". The addition function is handed the results, \
", (atomstring) "2", " and ", (atomstring) "4", ", and guess what...^^";
print_trans("(+ (+ 1 1) 4)",
"6");
new_line;
print "Actually, the addition and subtraction functions can take any number of arguments, \
not just two.^^";
print_trans("(+ 1 1 4)",
"6");
new_line;
print "What happens if you try to evaluate a list, and the first term doesn't evaluate \
to a function? You get errors, that's what. There are a couple of ways you can \
get this wrong. As your end-of-chapter exercise, meditate upon the following \
exchanges:^^";
print_trans("(1 2 3 4)",
"[Error: object is not a function: 1]");
print_trans("(spoggly 2 3 4)",
"[Error: undefined atom: spoggly]");
new_line;
print "Oops, one more thing. The empty list -- whether you write it as ", (atomstring) "()", " or ", (atomstring) "nil", " -- \
evaluates to itself. No function is called.^^";
print_trans("()",
"nil");
print_trans("nil",
"nil");
new_line;
];
[ Chapter5;
print_title ("5. Quoting Expressions");
print "What's an expression which evaluates to the atom ", (atomstring) "hello", "? We know that ", (atomstring) "hello", " doesn't; \
that produces an error.^^";
print_trans("hello",
"[Error: undefined atom: hello]");
new_line;
print "It sure would be handy if there were a way to produce an arbitrary atom. Well, \
watch:^^";
print_trans("(quote hello)",
"hello");
new_line;
print (atomstring) "quote", " acts sort of like a function which returns its argument unchanged. \
(Actually, it's not a function; it's a special piece of syntax. Can you see why? \
It breaks a rule of functions. Answer at the end of this chapter.) You can quote \
a list too:^^";
print_trans("(quote (+ 1 1))",
"(+ 1 1)");
new_line;
print (atomstring) "quote", " is so useful that you can abbreviate it, by skipping the parentheses and \
just writing a single quote mark.^^";
print_trans("'hello",
"hello");
print_trans("'(+ 1 1)",
"(+ 1 1)");
print_trans("'5",
"5");
print_trans("'+",
"+");
new_line;
print "That last example is important. It doesn't matter that ", (atomstring) "+", " has a value; the \
expression ", (atomstring) "'+", " -- that is, ", (atomstring) "(quote +)", " -- just evaluates to ", (atomstring) "+", ".^^";
print "End of the chapter time. Why is ", (atomstring) "quote", " not a function? Because when Scheme evaluates \
a function, it evaluates all the arguments before handing them in to the function. \
The argument to ", (atomstring) "quote", " is ", (emphstring) "not", " evaluated; it's handed straight in, so that it can \
be handed out unchanged. Watch this:^^";
print_trans("quote",
"[syntax]");
new_line;
print "Special syntax forms are applied the same way functions are, but they can have \
funkier effects, and they don't necessarily evaluate all their arguments. Keep track \
of this fact, because someday it'll unconfuse you about something.^^";
];
[ Chapter6;
print_title ("6. Defining Atoms");
print (atomstring) "+", " has a value which is automatically defined by the interpreter. Can we define \
values for atoms ourselves? Would I ask if the answer were no? Watch this:^^";
print_trans("(define x 5)",
"5");
new_line;
print (atomstring) "define", " is another piece of special syntax. It evaluates its third argument, and \
assigns the resulting value to its second argument, which must be an atom. (Why can't ",
(atomstring) "define", " be a function? Because all the arguments to a function are evaluated. If ", (atomstring) "define", " \
tried to evaluate its second argument, it would get an undefined atom error, because \
the atom hasn't been defined until ", (atomstring) "define", " defines it! Whew.) For clarity, ", (atomstring) "define", " \
returns the value it just assigned. We set ", (atomstring) "x", " to ", (atomstring) "5", "; now the expression ", (atomstring) "x", " evaluates \
to ", (atomstring) "5", ".^^";
print_trans("x",
"5");
print_trans("(+ x 1)",
"6");
new_line;
print "We don't have to use numbers, by the way. Let's set ", (atomstring) "x", " to be ", (atomstring) "bob", ":^^";
print_trans("(define x bob)",
"[Error: undefined atom: bob]");
new_line;
print "Oops -- forgot that the third value in a ", (atomstring) "define", " statement ", (emphstring) "is", " evaluated. We can't \
use the ", (emphstring) "atom", " ", (atomstring) "bob", "; we need to use an expression whose ", (emphstring) "value", " is ", (atomstring) "bob", ".^^";
print_trans("(define x 'bob)",
"bob");
print_trans("x",
"bob");
print_trans("(+ x 1)",
"[Error: +: non-numeric argument: bob]");
print_trans("'x",
"x");
new_line;
print "That works. Notice that we've replaced the earlier definition of ", (atomstring) "x", " as ", (atomstring) "5", ". Also note \
that addition righteously complains when we feed it an atom which isn't a number. And \
also also note that ", (atomstring) "'x", " is, still and always, just plain ", (atomstring) "x", ".^^";
print "You can replace the definitions of predefined functions, too. You could do ", (atomstring) "(define + 5)", ", \
for example. But I really don't recommend it. Reset the interpreter if you get carried \
away with this stuff.^^";
print "Last trick of the chapter. What's going on here?^^";
print_trans("(define addify +)",
"[function]");
print_trans("(addify 5 6)",
"11");
new_line;
];
[ Chapter7;
print_title ("7. List Chopping");
print "Back to lists. We often want to take them apart, see what makes them tick, and put \
together new ones. For that, we'll need some tools.^^";
print "A list can be split into two parts: its first term, and its everything else. Now, \
in Scheme (and Lisp), the first term is called the list's ", (emphstring) "car", ", and the everything \
else is called the list's ", (emphstring) "cdr", " (rhymes with ~wooder~, more or less.) Now every \
damn Lisp book in the universe explains why these things are called car and cdr, \
and I'm so sick of it I'm going to leave you in the dark. Go look it up, if you \
care.^^";
print "The car of ", (atomstring) "(a bb ccc)", " is ", (atomstring) "a", ". The cdr of ", (atomstring) "(a bb ccc)", " is ", (atomstring) "(bb ccc)", ". See that? The \
car is the first ", (emphstring) "term", "; the cdr is the ", (emphstring) "list", " minus the first term. Important, that. \
Now, this doesn't mean that the car of a list is always an atom. The car of \
", (atomstring) "((1 2) x y z)", " is ", (atomstring) "(1 2)", ". But the cdr of a list is always a list. It might be the \
empty list, though. The cdr of ", (atomstring) "(hello)", " is the empty list ", (atomstring) "nil", ".^^";
print "The empty list has neither a car nor a cdr.^^";
print (atomstring) "car", " and ", (atomstring) "cdr", " are pre-defined functions in Scheme. (Ok, I'm lying -- ", (atomstring) "car", " and ", (atomstring) "cdr", " \
are atoms. But they're defined to evaluate to functions, just like ", (atomstring) "+", " and ", (atomstring) "-", ". Get \
over it.)^^";
print_trans("(car '(a bb ccc))",
"a");
print_trans("(cdr '(a bb ccc))",
"(bb ccc)");
new_line;
print "Note the quote marks. What happens if you try to evaluate ", (atomstring) "(car (a bb cc))", "? Why?^^";
print (atomstring) "car", " and ", (atomstring) "cdr", " are well-wired to complain if you try to mess with their heads:^^";
print_trans("(car 'a)",
"[Error: car: bad argument: a]");
print_trans("(car '())",
"[Error: car: bad argument: nil]");
new_line;
print (atomstring) "a", " isn't a list, so it can't have a car or cdr; and the empty list, as we've said, \
has no car or cdr either.^^";
];
[ Chapter8;
print_title ("8. List Constructing");
print "We takes 'em apart, we puts 'em together. If you have a car and a cdr, you can \
construct a list. The ", (atomstring) "cons", " function does this.^^";
print_trans("(cons 'aaa '(bb c))",
"(aaa bb c)");
print_trans("(cons 'aaa nil)",
"(aaa)");
print_trans("(cons (car '(x y z z y)) (cdr '(x y z z y)))",
"(x y z z y)");
new_line;
print "That last example, dreadful as it appears, just demonstrates that you can take \
a single list apart into car and cdr, and reassemble it. Maybe it'd help if I \
print "I'm being tricky with that last one. If you try it yourself, make sure you've \
defined ", (atomstring) "magic-word", " the way I did a few paragraphs back. Also, see the difference \
a quote can make?^^";
];
[ Chapter9;
print_title ("9. Tests and Logic");
print "Some functions test a condition. The ", (atomstring) "=", " function, for example, tests to see if \
two numbers are equal:^^";
print_trans("(= 2 2)",
"t");
print_trans("(= 2 5)",
"nil");
new_line;
print "As you see, the convention in Scheme is to use ", (atomstring) "nil", " to signify falsity, and \
the atom ", (atomstring) "t", " to signify truth. (", (atomstring) "t", " evaluates to itself, by the way, just as ", (atomstring) "nil", " \
does. More convenience; you don't have to quote ", (atomstring) "t", " when you use it.) Actually, \
when a true/false value is required, you can generally supply any value; ", (atomstring) "nil", " \
will be counted as false, and anything else at all will be counted as true.^^";
print "The ", (atomstring) "<", ", ", (atomstring) ">", ", ", (atomstring) "<=", ", and ", (atomstring) ">=", " functions work as you'd expect. There are also some \
pre-defined functions which test other properties. ", (atomstring) "null?", " returns ", (atomstring) "t", " if its \
argument is ", (atomstring) "nil", ", and ", (atomstring) "nil", " otherwise. ", (atomstring) "list?", " returns ", (atomstring) "t", " if its argument is \
a list (including ", (atomstring) "nil", "), and ", (atomstring) "nil", " otherwise.^^";
print_trans("(null? nil)",
"t");
print_trans("(null? t)",
"nil");
print_trans("(null? 0)",
"nil");
print_trans("(list? nil)",
"t");
print_trans("(list? 1)",
"nil");
print_trans("(list? 'a)",
"nil");
print_trans("(list? '(a))",
"t");
new_line;
];
[ Chapter10;
print_title ("10. Comparisons");
print "The ", (atomstring) "=", " function compares numbers, but it produces an error if you try to \
use it on atoms or lists. For that, you need the ", (atomstring) "eqv?", " and ", (atomstring) "equal?", " functions. \
These are slightly different, and the difference is sort of technical and \
confusing, but it's historical. You want to learn Scheme, you have to know ", (atomstring) "eqv?", " \
and ", (atomstring) "equal?", " (In fact, real Scheme has ", (atomstring) "eq?", " as well, but the difference wasn't \
significant in this implementation, so I left it out.)^^";
print "For atoms (including numbers), they both work as you'd expect. ", (atomstring) "nil", ", also.^^";
print_trans("(eqv? 1 2)",
"nil");
print_trans("(eqv? 2 2)",
"t");
print_trans("(eqv? 'one 'two)",
"nil");
print_trans("(eqv? 'two 'two)",
"t");
print_trans("(eqv? 2 'two)",
"nil");
print_trans("(eqv? nil 'two)",
"nil");
print_trans("(eqv? nil nil)",
"t");
new_line;
print "(And ", (atomstring) "equal?", " would produce the same results.)^^";
print "For complex objects, such as lists and functions, things are trickier. If you \
hand two complex objects to ", (atomstring) "eqv?", ", you'll get ", (atomstring) "t", " only if they both stem from the \
same act of creation. (See, I told you it was confusing.)^^";
print_trans("(define greeting '(hi there))",
"(hi there)");
print_trans("(define aloha (list 'hi 'there))",
"(hi there)");
print_trans("(eqv? greeting greeting)",
"t");
print_trans("(eqv? greeting '(hi there))",
"nil");
print_trans("(eqv? greeting aloha)",
"nil");
print_trans("(eqv? '(hi there) '(hi there))",
"nil");
print_trans("(define hug greeting)",
"(hi there)");
print_trans("(eqv? greeting hug)",
"t");
print_trans("(eqv? aloha hug)",
"nil");
new_line;
print "How to put this... evaluating ", (atomstring) "'(hi there)", " creates a list of two atoms. Evaluating \
", (atomstring) "(list 'hi 'there)", " creates another list of two atoms. The lists have the same \
contents, but they're two separate objects. So they aren't ", (atomstring) "eqv?", " to each other. \
Now, when we do ", (atomstring) "(define hug greeting)", ", we take the value of ", (atomstring) "greeting", " and \
assign that very value to ", (atomstring) "hug", ", so they ", (emphstring) "are", " ", (atomstring) "eqv?", "... see? Well, maybe.^^";
print (atomstring) "equal?", ", on the other hand, works sensibly. Two lists are ", (atomstring) "equal?", " if they are \
the same length and each pair of terms is ", (atomstring) "equal?", ". Given the definitions above, \
you'll see that:^^";
print_trans("(equal? greeting greeting)",
"t");
print_trans("(equal? greeting '(hi there))",
"t");
print_trans("(equal? '(hi there) '(hi there))",
"t");
print_trans("(equal? greeting aloha)",
"t");
print_trans("(equal? greeting hug)",
"t");
print_trans("(equal? aloha hug)",
"t");
new_line;
print "What's the point of ", (atomstring) "eqv?", ", seeing how weirdly it works? It's faster. ", (atomstring) "equal?", " \
has to compare every element of a list, but ", (atomstring) "eqv?", " just compares two internal \
pointers.^^";
print "Up above I said that functions are as tricky as lists, when it comes to \
comparison. In fact, they're trickier. If two functions stem from the same \
act of creation, they're ", (atomstring) "eqv?", " and ", (atomstring) "equal?", ", just like lists. But otherwise, \
two functions are never ", (atomstring) "eqv?", " or ", (atomstring) "equal?", ", even if they do the exact same thing. \
(There's no good way to ", (emphstring) "tell", " if two functions do the exact same thing. I mean, \
there's no way for a ", (emphstring) "person", " to tell. Not in all cases. Computers, forget it.)^^";
];
[ Chapter11;
print_title ("11. Conditionals");
print "Scheme offers a nice assortment of conditional syntax forms. Since I'm a lazy \
bum, I've only implemented the most basic one, which is ", (atomstring) "cond", ". A ", (atomstring) "cond", " structure \
looks like this:^^";
print_multiatom(
" (cond",
" (test1 result1)",
" (test2 result2)",
" ...",
" )"
);
new_line;
print "I've written it on multiple lines, but that's just for clarity. The point is, \
you have a list of clauses, and each clause is a list of two expressions: a \
test and a result. The ", (atomstring) "cond", " syntax goes through the clauses, in order. For \
each one, it evaluates the test. If the test returns true (anything but ", (atomstring) "nil", "), \
it evaluates the result, and returns that value. If the test returns ", (atomstring) "nil", ", it \
goes on to the next clause. If all of the clauses return ", (atomstring) "nil", ", it returns ", (atomstring) "nil", ".^^";
print "If you're not sure what this means, look at it this way: in C, we'd write this \
as something like^^";
print_multiatom(
" if (test1) return result1;",
" else if (test2) return result2;",
" ...",
" else return nil;"
);
new_line;
print "If you want a final ~default~ clause, which will be used if all the others fail, \
you can use ", (atomstring) "(t defaultresult)", ". ", (atomstring) "t", " is always true, and that's the effect you want.^^";
print "You're still confused. Well, hark to the example.^^";
print_trans("(define val -25)",
"-25");
print_multitrans(
"(cond",
" ((> val 0) 'positive)",
" ((< val 0) 'negative)",
" (t 'zero)",
")"
);
print_trans(2, "negative");
new_line;
print "The conditional tests ", (atomstring) "val", " and returns one of the atoms ", (atomstring) "positive", ", ", (atomstring) "negative", ", or \
", (atomstring) "zero", ". (Again, we've typed it in on several lines. Notice that the interpreter \
prints a single-arrow prompt if you've started a list and not finished it yet; \
the expression won't be evaluated until you've typed it all in. As a bonus, the \
status line shows how many left parentheses are currently hanging open. Cool, \
huh?)^^";
];
[ Chapter12;
print_title ("12. Creating Functions");
print "There'd be no point to Scheme (or Lisp) if you couldn't define your own functions. \
You get a function when you evaluate a lambda-expression. ", (atomstring) "lambda", " is another piece \
of special syntax, and here's how you use it:^^";
print_trans("(lambda (n) (+ n 1))",
"[function]");
new_line;
print "See? A function! You can probably guess what it does, but let's check:^^";
print_trans("(define fred (lambda (n) (+ n 1)))",
"[function]");
print_trans("(fred 56)",
"57");
new_line;
print "First we define ", (atomstring) "fred", " to be our new function, and then we call it with ", (atomstring) "56", " as the \
argument. You don't need the definition, by the way. ", (atomstring) "(lambda (n) (+ n 1))", " \
evaluates to a function, so you can use it as the first term of a list, just like \
", (atomstring) "+", " or ", (atomstring) "car", ":^^";
print_trans("((lambda (n) (+ n 1)) 56)",
"57");
new_line;
print "It's a little hard to read, but count the parentheses and you'll see how it works.^^";
print "Time to be more specific. The ", (atomstring) "lambda", " syntax takes two arguments. The first must \
be a list of atoms, which are the names of the arguments of the function. The \
second is an expression which is evaluated to produce the function result.^^";
print "So when we call our little one-argument function, it's ", (emphstring) "kind", " of like we did the \
following:^^";
print_trans("(define n 56)",
"56");
print_trans("(+ n 1)",
"57");
new_line;
print "Only, not really. Function argument definitions are local, not global. The assignment \
of ", (atomstring) "56", " to ", (atomstring) "n", " is only visible ", (emphstring) "to the function", ". In the outside world, the real world, \
", (atomstring) "n", " isn't affected at all.^^";
print_trans("(define n 11)",
"11");
print_trans("(fred 93)",
"94");
print_trans("n",
"11");
new_line;
print "This is important. It's called static binding. Comp Sci types think it's really cool. \
It means that functions are all wrapped up in themselves -- they can't have side \
effects, and they can't be affected by outside influences (besides their arguments, \
of course.) Um, mostly. If you're careful. We'll get back to this, I hope.^^";
print "A function can have any number of arguments, including zero:^^";
print (atomstring) "let*", " is cool, but it doesn't allow you to do really funky recursive stuff, with \
several definitions that ", (emphstring) "really", " all reference each other. For that, you need \
", (atomstring) "letrec", ". ", (atomstring) "letrec", " has the same form as ", (atomstring) "let", " and ", (atomstring) "let*", ", but any of the assigned \
values can use any of the other atoms being bound. Sort of. The catch is, the \
atoms being bound can only be used ", (emphstring) "inside lambda-expressions.", " So this isn't \
legal:^^";
print_multitrans(
"(letrec",
" ((a a))",
" a",
")"
);
new_line;
print "This is bad because the ", (emphstring) "use", " of the atom being defined (the second ", (atomstring) "a", ", that \
is) isn't ~protected~ inside a lambda-expression. If you think this is an \
arbitrary restriction, consider: if it were legal, what the heck would it \
evaluate to?^^";
print "In case you were about to try this, by the way -- yes, I see you in the corner \
-- you'll find it doesn't produce an error; it returns ", (atomstring) "nil", ". If there were \
more terms being defined in the ", (atomstring) "letrec", ", the results could be even stranger. \
This is because I used a fairly unpleasant hack when writing the ", (atomstring) "letrec", " \
code. (A legal hack, honest. The Scheme standard says that this sort of \
print "Returns the list containing all but the first term of ", (atomstring) "v", ".^^";
print (atomstring) "(cons v w)", " : 2 arguments (the second a list)^";
print "Returns the list whose first term is ", (atomstring) "v", " and the rest of whose terms are \
the terms of ", (atomstring) "w", ".^^";
print (atomstring) "(length v)", " : 1 argument (a list)^";
print "Returns the number of terms in ", (atomstring) "v", ".^^";
print (atomstring) "(list v ...)", " : 0 or more arguments^";
print "Returns the list whose terms are the given arguments.^^";
print (atomstring) "(not v)", " : 1 argument^";
print "Returns ", (atomstring) "t", " if ", (atomstring) "v", " is ", (atomstring) "nil", ", and ", (atomstring) "nil", " otherwise.^^";
print (atomstring) "(eqv? v w)", " : 2 arguments^";
print "Returns ", (atomstring) "t", " if ", (atomstring) "v", " and ", (atomstring) "w", " are both ", (atomstring) "nil", ", or are the same atom, or were created at \
the same time. Returns ", (atomstring) "nil", " otherwise.^^";
print (atomstring) "(equal? v w)", " : 2 arguments^";
print "Returns ", (atomstring) "t", " if ", (atomstring) "v", " and ", (atomstring) "w", " are ", (atomstring) "eqv?", ", or are lists of the same length all of whose \
terms are ", (atomstring) "equal?", ".^^";
print (atomstring) "(null? v)", " : 1 argument^";
print "Returns ", (atomstring) "t", " if ", (atomstring) "v", " is ", (atomstring) "nil", ", and ", (atomstring) "nil", " otherwise. (Yes, this is the same as ", (atomstring) "not", ".)^^";
print (atomstring) "(list? v)", " : 1 argument^";
print "Returns ", (atomstring) "t", " if ", (atomstring) "v", " is a list, including ", (atomstring) "nil", ". Returns ", (atomstring) "nil", " if ", (atomstring) "v", " is anything \
else, such as an atom or function.^^";
print (atomstring) "(= v ...)", " : 1 or more arguments (all numbers)^";
print "Returns ", (atomstring) "t", " if all the arguments are the same number. If there is only one \